﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace GameLib.Utils
{
	public static class TypeNameTable
	{
		public static Type GetType(string name)
		{
			return GetType(name, true);
		}

		public static Type GetType(string name, bool throwError)
		{
			Type type;
			if (RegisteredTypes.TryGetValue(name, out type))
			{
				return type;
			}
			else 
			{
				if (throwError)
					throw new Exception(string.Format("Type {0} not found!", name));
				return null;
			}
		}

		public static void LoadAssembly(string assemblyName)
		{
			Assembly assembly = Assembly.Load(assemblyName);

			foreach (Type type in assembly.GetTypes())
			{
				object[] selfAttrs = type.GetCustomAttributes(typeof(TypeNameRegistrationAttribute), false);

				if (selfAttrs.Length > 0)
				{
					TypeNameRegistrationAttribute attr = selfAttrs[0] as TypeNameRegistrationAttribute;

					if (!attr.UseOnlyAliases)
						RegisterTypeName(type, type.Name);
					else if (attr.Aliases.Length == 0)
						throw new Exception(string.Format("Type {0} indicating to register only with Aliases but the given aliases array has zero elements in it", type));

					foreach (string alias in attr.Aliases)
					{
						RegisterTypeName(type, alias);
					}
				}
				else
				{
					object[]  ancestorAttrs = type.GetCustomAttributes(typeof(TypeNameRegistrationAttribute), true);

					if (ancestorAttrs.Length > 0)
					{
						TypeNameRegistrationAttribute attr = ancestorAttrs[0] as TypeNameRegistrationAttribute;

						if (attr.RegisterDecendants)
							RegisterTypeName(type, type.Name);
					}
				}
			}
		}

		private static void RegisterTypeName(Type type, string name)
		{
			Type existingType;
			if (RegisteredTypes.TryGetValue(name, out existingType))
			{
				throw new Exception(string.Format("Trying to register type {0} with name {1}, but the name has been taken by another type {2}", type, name, existingType));
			}

			RegisteredTypes[name] = type;
		}

		private static Dictionary<string, Type> RegisteredTypes = new Dictionary<string, Type>();
	}

	[AttributeUsage(AttributeTargets.Class)]
	public class TypeNameRegistrationAttribute : Attribute
	{
		public bool RegisterDecendants
		{
			get;
			set;
		}

		public bool UseOnlyAliases
		{
			get;
			set;
		}

		public string[] Aliases
		{
			get;
			private set;
		}

		public TypeNameRegistrationAttribute(params string[] aliases)
		{
			Aliases = aliases;
			RegisterDecendants = false;
			UseOnlyAliases = false;
		}
	}
}
